package com.tanhua.dubbo.server.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.convert.Convert;
import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.pojo.UserLike;
import com.tanhua.dubbo.server.vo.PageInfo;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service(version = "1.0.0")
public class UserLikeApiImpl implements UserLikeApi {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    public static final String LIKE_REDIS_KEY_PREFIX = "USER_LIKE_";

    public static final String NOT_LIKE_REDIS_KEY_PREFIX = "USER_NOT_LIKE_";


    /**
     * 喜欢
     *
     * @param userId
     * @param likeUserId
     * @return
     */
    @Override
    public Boolean likeUser(Long userId, Long likeUserId) {
        //判断该用户是否已经喜欢，如果已经喜欢就返回
        if (this.isLike(userId, likeUserId)) {
            return false;
        }

        UserLike userLike = new UserLike();
        userLike.setId(ObjectId.get());
        userLike.setUserId(userId);
        userLike.setLikeUserId(likeUserId);
        userLike.setCreated(System.currentTimeMillis());

        //将数据存储到MongoDB
        this.mongoTemplate.save(userLike);

        //用户的喜欢数据保存到redis
        //用户1：key -> USER_LIKE_1 , value -> 2, "1"
        //用户1：key -> USER_LIKE_1 , value -> 3, "1"
        //用户2：key -> USER_LIKE_2 , value -> 4, "1"
        String redisKey = this.getLikeRedisKey(userId);
        String hashKey = String.valueOf(likeUserId);
        this.redisTemplate.opsForHash().put(redisKey, hashKey, "1");

        //判断，喜欢的用户是否在不喜欢的列表中，如果在，就需要删除数据
        if (this.isNotLike(userId, likeUserId)) {
            redisKey = this.getNotLikeRedisKey(userId);
            this.redisTemplate.opsForHash().delete(redisKey, hashKey);
        }

        return true;
    }

    /**
     * 获取喜欢数据的redis key
     *
     * @param userId
     * @return
     */
    private String getLikeRedisKey(Long userId) {
        return LIKE_REDIS_KEY_PREFIX + userId;
    }

    /**
     * 获取不喜欢数据的redis key
     *
     * @param userId
     * @return
     */
    private String getNotLikeRedisKey(Long userId) {
        return NOT_LIKE_REDIS_KEY_PREFIX + userId;
    }

    /**
     * 是否喜欢
     *
     * @param userId
     * @param likeUserId
     * @return
     */
    @Override
    public Boolean isLike(Long userId, Long likeUserId) {
        String redisKey = this.getLikeRedisKey(userId);
        String hashKey = String.valueOf(likeUserId);
        // 我的喜欢列表中是否有你的id  如果有就说明已经喜欢
        return this.redisTemplate.opsForHash().hasKey(redisKey, hashKey);
    }

    /**
     * 是否不喜欢
     *
     * @param userId
     * @param likeUserId
     * @return
     */
    @Override
    public Boolean isNotLike(Long userId, Long likeUserId) {
        String redisKey = this.getNotLikeRedisKey(userId);
        String hashKey = String.valueOf(likeUserId);
        return this.redisTemplate.opsForHash().hasKey(redisKey, hashKey);
    }

    @Override
    public Boolean notLikeUser(Long userId, Long likeUserId) {
        //判断用户是否已经不喜欢，如果已经不喜欢，就返回
        if (this.isNotLike(userId, likeUserId)) {
            return false;
        }

        // 第一件事:  把取消喜欢的人给添加到不喜欢列表
        // 第二件事:  把取消喜欢的人从MongoDB中删除
        // 第三件事: 把取消喜欢的人从我的喜欢列表中删除

        // 将用户保存到不喜欢列表中
        String redisKey = this.getNotLikeRedisKey(userId);
        String hashKey = String.valueOf(likeUserId);
        this.redisTemplate.opsForHash().put(redisKey, hashKey, "1");

        //判断用户是否在喜欢列表中，如果存在的话，需要删除数据
        if (this.isLike(userId, likeUserId)) {
            //删除MongoDB数据
            Query query = Query.query(Criteria
                    .where("userId").is(userId)
                    .and("likeUserId").is(likeUserId)
            );
            this.mongoTemplate.remove(query, UserLike.class);

            //删除redis中的数据
            redisKey = this.getLikeRedisKey(userId);
            this.redisTemplate.opsForHash().delete(redisKey, hashKey);
        }

        return true;
    }

    /**
     * 是否相互喜欢
     *
     * @param userId
     * @param likeUserId
     * @return
     */
    @Override
    public Boolean isMutualLike(Long userId, Long likeUserId) {
        return this.isLike(userId, likeUserId)
                && this.isLike(likeUserId, userId);
    }

    /**
     * 在redis中查询自己喜欢的人的列表
     *
     * @param userId
     * @return
     */
    @Override
    public List<Long> queryLikeList(Long userId) {
        // 查询redis
        String redisKey = this.getLikeRedisKey(userId);
        Set<Object> keys = this.redisTemplate.opsForHash().keys(redisKey);
        if (CollUtil.isEmpty(keys)) {
            return ListUtil.empty();
        }
        // 如果Redis中查询为空 我们还要再从MongoDB中查询一遍
        List<Long> result = new ArrayList<>(keys.size());
        keys.forEach(o -> result.add(Convert.toLong(o)));
        return result;
    }

    @Override
    public List<Long> queryNotLikeList(Long userId) {
        // 查询redis
        String redisKey = this.getNotLikeRedisKey(userId);
        Set<Object> keys = this.redisTemplate.opsForHash().keys(redisKey);
        if (CollUtil.isEmpty(keys)) {
            return ListUtil.empty();
        }

        List<Long> result = new ArrayList<>(keys.size());
        keys.forEach(o -> result.add(Convert.toLong(o)));
        return result;
    }

    /**
     * 查询相互喜欢数
     * 实现2种方式：第一种：查询redis，第二种：查询MongoDB
     * 建议：优先使用redis查询，其次考虑使用Mongodb
     * <p>
     * 喜欢: 首先保存到了redis中 然后保存到MongoDB中
     * 相互喜欢:  先根据自己的id查询到自己喜欢的人的id集合    [94,28,24...]
     * 要遍历我喜欢的人的id  分别去查询他们是否喜欢我
     * Redis:  Key_1 {"2":"1","3":"1"...}
     * 分别查询我喜欢的人的redisKey  包含我的id
     *
     * @param userId
     * @return
     */
    @Override
    public Long queryMutualLikeCount(Long userId) {
        //查询我的喜欢列表  10 , 12 , 15, 19
        List<Long> likeUserIdList = this.queryLikeList(userId);
        Long count = 0L;
        for (Long likeUserId : likeUserIdList) {
            // likeUserId 我喜欢人的id redisKey是我喜欢人的redis中的Key
            String redisKey = this.getLikeRedisKey(likeUserId);
            // userId 是我自己的ID
            String hashKey = String.valueOf(userId);
            //“别人” 的喜欢列表中是否有 “我”
            // xxx_2 1
            if (this.redisTemplate.opsForHash().hasKey(redisKey, hashKey)) {
                count++;
            }
        }
        //  如果 count == 0  我们还要再从MongoDB中进行查询
        return count;
    }

    /**
     * 我喜欢的人的数据统计
     *
     * @param userId
     * @return
     */
    @Override
    public Long queryLikeCount(Long userId) {
        // 如果这边从Redis中查询的数据为空 最好还是要走MongoDB查询
        String redisKey = this.getLikeRedisKey(userId);
        return this.redisTemplate.opsForHash().size(redisKey);
    }

    /**
     * 喜欢我的人的数量统计
     *
     * @param userId
     * @return
     */
    @Override
    public Long queryFanCount(Long userId) {
        // 无法通过redis查询完成，必须从Mongodb中查询
        // 如果我们在redis中保存了 这边也可以通过Redis进行查询
        // A --> B 在A的喜欢列表中添加B  同时 在B的粉丝列表中添加A
        // C --> A 在C的喜欢列表中添加A  同时 在A的粉丝列表中添加C
        // D --> A 在D的喜欢列表中添加A  同时 在A的粉丝列表中添加D
        Query query = Query.query(Criteria.where("likeUserId").is(userId));
        return this.mongoTemplate.count(query, UserLike.class);
    }

    /**
     * 查询互相喜欢的人的列表
     *
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<UserLike> queryMutualLikeList(Long userId, Integer page, Integer pageSize) {
        // redis 我喜欢的人的id给存储起来   redis存储的一定是高频查询的数据  选择好适合的数据格式
        //查询我的喜欢列表
        // userId 自己的id
        // 从redis中查询自己喜欢人的列表   我喜欢的人的列表  1   [23,34,14,68]
        List<Long> userLikeIdList = this.queryLikeList(userId);

        //查询喜欢我的人
        // 再从mongodb中查询喜欢我的人
        Query query = Query.query(Criteria.where("userId").in(userLikeIdList)
                .and("likeUserId").is(userId));
        return this.queryList(query, page, pageSize);
    }

    /**
     * 查询我喜欢的人的列表
     *
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<UserLike> queryLikeList(Long userId, Integer page, Integer pageSize) {
        Query query = Query.query(Criteria.where("userId").is(userId));
        return this.queryList(query, page, pageSize);
    }

    /**
     * 喜欢我的人的列表
     *
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<UserLike> queryFanList(Long userId, Integer page, Integer pageSize) {
        Query query = Query.query(Criteria.where("likeUserId").is(userId));
        return this.queryList(query, page, pageSize);
    }

    private PageInfo<UserLike> queryList(Query query, Integer page, Integer pageSize) {
        //设置分页
        PageRequest pageRequest = PageRequest.of(page - 1, pageSize,
                Sort.by(Sort.Order.desc("created")));
        query.with(pageRequest);

        List<UserLike> userLikeList = this.mongoTemplate.find(query, UserLike.class);

        PageInfo<UserLike> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page);
        pageInfo.setPageSize(pageSize);
        pageInfo.setRecords(userLikeList);

        return pageInfo;
    }
}
